import {
  ref,
  computed,
  unref,
  nextTick,
  type ExtractPropTypes,
  type InjectionKey,
  type PropType,
  type MaybeRef,
  watch,
} from 'vue';

import { cloneDeep } from 'lodash-es';
import { STATES, type State } from './constant';
import {
  useDataSource,
  raw,
  type DataSource,
  type DataSourceItem,
} from './data-source';
import { type TableContext as ExtraTableContext } from '../../extra-table/src/table/table';

export type EventData = {
  data: any[];
  row: any;
  index: number;
};

// 返回状态的助手函数
export type StateMethod = (state: any) => State;
// 展开行时调用的方法，用于给extra-table设置数据
export type ExpandMethod = (
  data: EventData,
  extraTableContext?: ExtraTableContext
) => void;
export type AddExpandMethod = (
  data: EventData,
  extraTableContext?: ExtraTableContext
) => void;

export const props = {
  dataSource: {
    type: Object as PropType<DataSource<unknown>>,
    default: () => useDataSource(),
  },
  data: {
    type: Array as PropType<MaybeRef<unknown[]> | null>,
    default: null,
  },
  border: Boolean,
  shadow: Boolean,
  defaultValue: {
    type: Object,
    default: () => ({}),
  },

  stateField: {
    type: String,
    default: 'state',
  },
  stateMethod: {
    type: Function as PropType<StateMethod>,
    default: (state: any) => {
      if (Object.keys(STATES).includes(state)) {
        return STATES[state as State];
      }
      return 'view';
    },
  },

  addMethod: {
    type: Function as PropType<() => any | undefined>,
    default: undefined,
  },
  expandMethod: {
    type: Function as PropType<ExpandMethod>,
    default: undefined,
  },
  addExpandMethod: {
    type: Function as PropType<AddExpandMethod>,
    default: undefined,
  },
};

export type Expand = (
  data: EventData,
  extraTableContext?: ExtraTableContext
) => void;
export type Shrink = (
  data: EventData,
  extraTableContext?: ExtraTableContext
) => void;
export type ExpandSubmit = (
  data: EventData,
  extraTableContext?: ExtraTableContext
) => void;
export type ExpandCancel = (
  data: EventData,
  extraTableContext?: ExtraTableContext
) => void;
export type AddExpand = (
  data: EventData,
  extraTableContext?: ExtraTableContext
) => void;
export type Edit = (data: EventData) => void;
export type Save = (data: EventData) => void;
export type Add = (data: EventData) => void;
export type Copy = (data: EventData) => void;
export type Del = (data: EventData) => void;

export const emits = {
  expand: (data: EventData, extraTableContext?: ExtraTableContext) =>
    true,

  shrink: (data: EventData, extraTableContext?: ExtraTableContext) =>
    true,

  expandSubmit: (
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => true,

  expandCancel: (
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => true,

  edit: (data: EventData) => true,
  save: (data: EventData) => true,
  add: (data: EventData) => true,
  copy: (data: EventData) => true,
  del: (data: EventData) => true,

  addExpand: (
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => true,
};

type EasyTableProps = ExtractPropTypes<typeof props>;

// type Emits = { (event: keyof typeof emits, arg: any): void };
type Emits = ((event: 'edit', data: EventData) => void) &
  ((event: 'save', data: EventData) => void) &
  ((event: 'add', data: EventData) => void) &
  ((event: 'copy', data: EventData) => void) &
  ((event: 'del', data: EventData) => void) &
  ((
    event: 'expand',
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => void) &
  ((
    event: 'shrink',
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => void) &
  ((
    event: 'expandSubmit',
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => void) &
  ((
    event: 'expandCancel',
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => void) &
  ((
    event: 'addExpand',
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => void);

export function useEasyTableContext(
  $props: EasyTableProps,
  $emits: Emits
) {
  const {
    dataSource,
    data,
    defaultValue,
    stateMethod,
    stateField,
    expandMethod,
    addExpandMethod,
  } = $props;

  //TODO 状态转换先凑合用，回头给DataSource加上format方法
  if (data) {
    initData(unref(data));
    // let unrefed = unref(data);
    // unrefed = unrefed.map((e) => {
    //   // @ts-ignore
    //   e.__state__ = stateMethod(e[stateField]);
    //   return e;
    // });
    // dataSource.value = unrefed;
  }

  watch(
    () => $props.data,
    () => {
      initData(unref($props.data ?? []));
    }
  );

  function initData(data: any[]) {
    const newData = cloneDeep(data).map((e) => {
      (e as DataSourceItem).__state__ = stateMethod(e[stateField]);
      return e;
    });
    dataSource.value = newData;
  }

  const setData = (value: any[]) => initData(unref(value));
  const getData = () => dataSource.raw();

  const expandedKeys = ref<string[]>([]);
  const addExpandedKey = (key: string) => {
    if (expandedKeys.value.includes(key)) return;
    expandedKeys.value.push(key);
  };
  const delExpandedKey = (key: string) => {
    const index = expandedKeys.value.findIndex(
      // @ts-ignore
      (e) => e.__id__ === key
    );
    // console.log('🚀 ~ delExpandedKey ~ index:', index);
    expandedKeys.value.splice(index, 1);
  };
  const isExpand = (key: string) => {
    return expandedKeys.value.includes(key);
  };

  const states = computed(() =>
    dataSource.value.map((e) => e.__state__)
  );
  const setState = (index: number, state: State) => {
    dataSource.update({ __state__: state }, index);
  };

  const edit = (data: EventData) => {
    $emits('edit', {
      data: raw(data.data),
      row: raw(data.row),
      index: data.index,
    });
    setState(data.index, 'edit');
  };
  const save = (data: EventData) => {
    $emits('save', {
      data: raw(data.data),
      row: raw(data.row),
      index: data.index,
    });
    setState(data.index, 'view');
  };
  const add = async (
    data: EventData = { data: [], row: null, index: -1 }
  ) => {
    let newItem = defaultValue;
    if ($props.addMethod) {
      newItem = await $props.addMethod();
    }
    dataSource.insert(newItem);
    $emits('add', {
      data: raw(data.data),
      row: raw(data.row),
      index: data.index,
    });
  };
  const copy = (data: EventData) => {
    $emits('copy', {
      data: raw(data.data),
      row: raw(data.row),
      index: data.index,
    });
    const index = data.index;
    dataSource.copy(index);
  };
  const del = (data: EventData) => {
    $emits('del', {
      data: raw(data.data),
      row: raw(data.row),
      index: data.index,
    });
    dataSource.remove(data.index);
  };

  const doExpand = (
    data: EventData
    // extraContext?: ExtraTableContext,
    // callback?: Function
  ) => {
    return new Promise((resolve) => {
      const id = data.row.__id__;
      addExpandedKey(id);
      // 如果已展开id数组中没有该id（未展开）则添加
      nextTick(async () => {
        const extraContext = getExtra(id);

        // console.log('expandMethod start');
        await expandMethod?.(
          {
            data: raw(data.data),
            row: raw(data.row),
            index: data.index,
          },
          extraContext
        );
        // console.log('expandMethod end');

        $emits(
          'expand',
          {
            data: raw(data.data),
            row: raw(data.row),
            index: data.index,
          },
          extraContext
        );
        resolve(true);
      });
    });
  };

  // 点击addExpand按钮
  // 1.如果未展开则展开后执行expand-method
  // 2.然后执行 add-expand-method
  const addExpand = async (
    data: EventData,
    extraContext?: ExtraTableContext
  ) => {
    console.log('addExpand');

    const id = data.row.__id__;
    if (!id) return;
    // addExpandedKey(data.row.__id__);
    // nextTick(() => {
    //   extraContext = getExtra(data.row.__id__);
    //   $emits('addExpand', data, extraContext);
    // });

    if (!isExpand(id)) {
      await doExpand(data);
    }
    extraContext = getExtra(data.row.__id__);
    await addExpandMethod?.(
      {
        data: raw(data.data),
        row: raw(data.row),
        index: data.index,
      },
      extraContext
    );
  };

  const expand = (
    data: EventData
    // extraContext?: ExtraTableContext
  ) => {
    doExpand(data);

    // addExpandedKey(data.row.__id__);
    // // 如果已展开id数组中没有该id（未展开）则添加
    // nextTick(async () => {
    //   if (expandMethod) {
    //     console.log('expand start');
    //     await expandMethod(data, extraContext);
    //     console.log('expand end');
    //   }
    //   $emits('expand', data, extraContext);
    // });
  };
  const shrink = (
    data: EventData,
    extraContext?: ExtraTableContext
  ) => {
    $emits(
      'shrink',
      {
        data: raw(data.data),
        row: raw(data.row),
        index: data.index,
      },
      extraContext
    );
    delExpandedKey(data.row.__id__);
  };

  const expandSubmit = (
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => {
    $emits(
      'expandSubmit',
      {
        data: raw(data.data),
        row: raw(data.row),
        index: data.index,
      },
      extraTableContext
    );
  };
  const expandCancel = (
    data: EventData,
    extraTableContext?: ExtraTableContext
  ) => {
    $emits(
      'expandCancel',
      {
        data: raw(data.data),
        row: raw(data.row),
        index: data.index,
      },
      extraTableContext
    );
  };

  const extrasMap: Map<string, ExtraTableContext> = new Map();
  function registryExtra(extra: ExtraTableContext, id: string) {
    extrasMap.set(id, extra);
  }
  function getExtra(id?: string) {
    if (id) {
      return extrasMap.get(id);
    }
  }

  // // 提供给refs调用
  // function addItem(item?: any) {
  //   let newItem;
  //   if (item) {
  //     newItem = cloneDeep(item) as DataSourceItem;
  //   } else {
  //     newItem = cloneDeep(defaultValue);
  //   }

  //   // @ts-ignore
  //   newItem.__state__ = stateMethod(newItem[stateField]);
  //   dataSource.insert(newItem);
  // }

  return {
    tableData: dataSource,
    getData,
    setData,

    dataSource,
    states,

    edit,
    save,
    add,
    copy,
    del,
    addExpand,

    expand,
    shrink,
    expandSubmit,
    expandCancel,

    expandedKeys,
    addExpandedKey,
    delExpandedKey,

    registryExtra,
    getExtra,

    // provide
    // addItem,
  };
}

type EasyTableContext = ReturnType<typeof useEasyTableContext>;

export const easyTableContextKey: InjectionKey<EasyTableContext> =
  Symbol('easy-table');
